using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using Newtonsoft.Json;
using System.Net;
using System.IO;

namespace MeltingSnowballs.SmugMugAPI
{
    public class JSONSmugMugAPI : ISmugMugAPI
    {
        const string API_URL = "http://api.smugmug.com/hack/json/1.2.0/";
        const string SECURE_API_URL = "https://api.smugmug.com/hack/json/1.2.0/";
        const string UPLOAD_URL = "http://upload.smugmug.com/hack/json/1.2.0/";
        const string API_VERSION = "1.2.0";

        #region ISmugMugAPI Members

        public SMLoginInfo loginWithPassword(string emailAddress, string password, string apiKey)
        {
            JSONLoginInfo loginInfo = new JSONLoginInfo();

            StringBuilder url = new StringBuilder(SECURE_API_URL + "?method=smugmug.login.withPassword");
            appendArgumentToUrl(url, "EmailAddress", emailAddress);
            appendArgumentToUrl(url, "Password", password);
            appendArgumentToUrl(url, "APIKey", apiKey);

            loginInfo = executeWebMethod<JSONLoginInfo>(url.ToString());
            return loginInfo.Login;
        }

        public SMLoginInfo loginWithHash(string userID, string passwordHash, string apiKey)
        {
            JSONLoginInfo loginInfo = new JSONLoginInfo();

            StringBuilder url = new StringBuilder(SECURE_API_URL + "?method=smugmug.login.withHash");
            appendArgumentToUrl(url, "UserID", userID);
            appendArgumentToUrl(url, "PasswordHash", passwordHash);
            appendArgumentToUrl(url, "APIKey", apiKey);

            loginInfo = executeWebMethod<JSONLoginInfo>(url.ToString());
            return loginInfo.Login;
        }

        public SMSessionInfo loginAnonymously(string apiKey)
        {
            JSONLoginInfo loginInfo = new JSONLoginInfo();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.login.anonymously");
            appendArgumentToUrl(url, "APIKey", apiKey);

            loginInfo = executeWebMethod<JSONLoginInfo>(url.ToString());
            return loginInfo.Login.Session;
        }

        public void logout(string sessionID)
        {
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.logout");
            appendArgumentToUrl(url, "SessionID", sessionID);

            executeWebMethod(url.ToString());
        }

        public SMUserTreeCategory[] getUsersTree(string sessionID, string nickName, bool heavy, string sitePassword)
        {
            JSONUserTreeCategories categories = new JSONUserTreeCategories();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.users.getTree");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "NickName", nickName);
            appendArgumentToUrl(url, "Heavy", heavy ? "TRUE" : null);
            appendArgumentToUrl(url, "SitePassword", sitePassword);

            categories = executeWebMethod<JSONUserTreeCategories>(url.ToString());
            return categories.Categories;
        }

        public SMAlbumStats[] getUsersTransferStats(string sessionID, int month, int year)
        {
            JSONUserTransferStats transferStats = new JSONUserTransferStats();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.users.getTransferStats");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "Month", month.ToString());
            appendArgumentToUrl(url, "Year", year.ToString());

            transferStats = executeWebMethod<JSONUserTransferStats>(url.ToString());
            return transferStats.Albums;
        }

        public SMAlbum[] getAlbums(string sessionID, string nickName, bool heavy, string sitePassword)
        {
            JSONAlbums albums = new JSONAlbums();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.get");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "NickName", nickName);
            appendArgumentToUrl(url, "Heavy", heavy ? "TRUE" : null);
            appendArgumentToUrl(url, "SitePassword", sitePassword);

            albums = executeWebMethod<JSONAlbums>(url.ToString());
            return albums.Albums;
        }

        public SMAlbumInfo getAlbumInfo(string sessionID, int albumID, string password, string sitePassword)
        {
            JSONAlbumInfo albumInfo = new JSONAlbumInfo();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.getInfo");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "AlbumID", albumID.ToString());
            appendArgumentToUrl(url, "Password", password);
            appendArgumentToUrl(url, "SitePassword", sitePassword);

            albumInfo = executeWebMethod<JSONAlbumInfo>(url.ToString());
            return albumInfo.Album;
        }

        public int createAlbum(string sessionID, string title, int categoryID, SMAlbumSettings settings)
        {
            JSONAlbumID albumID = new JSONAlbumID();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.create");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "Title", title);
            appendArgumentToUrl(url, "CategoryID", categoryID.ToString());
            appendArgumentToUrl(url, "AlbumTemplateID", settings.AlbumTemplateID);
            appendArgumentToUrl(url, "SubCategoryID", settings.SubCategoryID);
            appendArgumentToUrl(url, "CommunityID", settings.CommunityID);
            appendArgumentToUrl(url, "Description", settings.Description);
            appendArgumentToUrl(url, "Keywords", settings.Keywords);
            appendArgumentToUrl(url, "Password", settings.Password);
            appendArgumentToUrl(url, "PasswordHint", settings.PasswordHint);
            appendArgumentToUrl(url, "Position", settings.Position);
            appendArgumentToUrl(url, "SortMethod", settings.SortMethod);
            appendArgumentToUrl(url, "SortDirection", settings.SortDirection);
            appendArgumentToUrl(url, "Public", settings.Public);
            appendArgumentToUrl(url, "Filenames", settings.Filenames);
            appendArgumentToUrl(url, "Comments", settings.Comments);
            appendArgumentToUrl(url, "External", settings.External);
            appendArgumentToUrl(url, "EXIF", settings.EXIF);
            appendArgumentToUrl(url, "Share", settings.Share);
            appendArgumentToUrl(url, "Printable", settings.Printable);
            appendArgumentToUrl(url, "Originals", settings.Originals);
            appendArgumentToUrl(url, "FamilyEdit", settings.FamilyEdit);
            appendArgumentToUrl(url, "FriendEdit", settings.FriendEdit);
            appendArgumentToUrl(url, "HideOwner", settings.HideOwner);
            appendArgumentToUrl(url, "Header", settings.Header);
            appendArgumentToUrl(url, "TemplateID", settings.TemplateID);
            appendArgumentToUrl(url, "Larges", settings.Larges);
            appendArgumentToUrl(url, "DefaultColor", settings.DefaultColor);
            appendArgumentToUrl(url, "Clean", settings.Clean);
            appendArgumentToUrl(url, "Protected", settings.Protected);
            appendArgumentToUrl(url, "Watermarking", settings.Watermarking);
            appendArgumentToUrl(url, "ProofDays", settings.ProofDays);
            appendArgumentToUrl(url, "Backprinting", settings.Backprinting);
            appendArgumentToUrl(url, "SmugSearchable", settings.SmugSearchable);
            appendArgumentToUrl(url, "WorldSearchable", settings.WorldSearchable);

            albumID = executeWebMethod<JSONAlbumID>(url.ToString());
            return albumID.Album.id;
        }

        public int changeAlbumSettings(string sessionID, int albumID, SMAlbumSettings settings)
        {
            JSONResult result = new JSONResult();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.changeSettings");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "AlbumID", albumID);
            appendArgumentToUrl(url, "AlbumTemplateID", settings.AlbumTemplateID);
            appendArgumentToUrl(url, "SubCategoryID", settings.SubCategoryID);
            appendArgumentToUrl(url, "CommunityID", settings.CommunityID);
            appendArgumentToUrl(url, "Description", settings.Description);
            appendArgumentToUrl(url, "Keywords", settings.Keywords);
            appendArgumentToUrl(url, "Password", settings.Password);
            appendArgumentToUrl(url, "PasswordHint", settings.PasswordHint);
            appendArgumentToUrl(url, "Position", settings.Position);
            appendArgumentToUrl(url, "SortMethod", settings.SortMethod);
            appendArgumentToUrl(url, "SortDirection", settings.SortDirection);
            appendArgumentToUrl(url, "Public", settings.Public);
            appendArgumentToUrl(url, "Filenames", settings.Filenames);
            appendArgumentToUrl(url, "Comments", settings.Comments);
            appendArgumentToUrl(url, "External", settings.External);
            appendArgumentToUrl(url, "EXIF", settings.EXIF);
            appendArgumentToUrl(url, "Share", settings.Share);
            appendArgumentToUrl(url, "Printable", settings.Printable);
            appendArgumentToUrl(url, "Originals", settings.Originals);
            appendArgumentToUrl(url, "FamilyEdit", settings.FamilyEdit);
            appendArgumentToUrl(url, "FriendEdit", settings.FriendEdit);
            appendArgumentToUrl(url, "HideOwner", settings.HideOwner);
            appendArgumentToUrl(url, "Header", settings.Header);
            appendArgumentToUrl(url, "TemplateID", settings.TemplateID);
            appendArgumentToUrl(url, "Larges", settings.Larges);
            appendArgumentToUrl(url, "DefaultColor", settings.DefaultColor);
            appendArgumentToUrl(url, "Clean", settings.Clean);
            appendArgumentToUrl(url, "Protected", settings.Protected);
            appendArgumentToUrl(url, "Watermarking", settings.Watermarking);
            appendArgumentToUrl(url, "ProofDays", settings.ProofDays);
            appendArgumentToUrl(url, "Backprinting", settings.Backprinting);
            appendArgumentToUrl(url, "SmugSearchable", settings.SmugSearchable);
            appendArgumentToUrl(url, "WorldSearchable", settings.WorldSearchable);

            result = executeWebMethod<JSONResult>(url.ToString());
            return 0;
        }

        public bool reSortAlbum(string sessionID, int albumID, string by, string direction)
        {
            JSONAlbumReSortResponse response = new JSONAlbumReSortResponse();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.reSort");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "AlbumID", albumID);
            appendArgumentToUrl(url, "By", by);
            appendArgumentToUrl(url, "Direction", direction);

            response = executeWebMethod<JSONAlbumReSortResponse>(url.ToString());
            return response.Album;
        }

        public bool deleteAlbum(string sessionID, int albumID)
        {
            JSONAlbumDeleteResponse response = new JSONAlbumDeleteResponse();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.delete");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "AlbumID", albumID);

            response = executeWebMethod<JSONAlbumDeleteResponse>(url.ToString());
            return response.Album;
        }

        public SMAlbumStats getAlbumStats(string sessionID, int albumID, int month, int year, bool heavy)
        {
            JSONAlbumStats albumStats = new JSONAlbumStats();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.albums.getStats");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "AlbumID", albumID.ToString());
            appendArgumentToUrl(url, "Month", month.ToString());
            appendArgumentToUrl(url, "Year", year.ToString());
            appendArgumentToUrl(url, "Heavy", heavy ? "TRUE" : null);

            albumStats = executeWebMethod<JSONAlbumStats>(url.ToString());
            return albumStats.Album;
        }

        public SMAlbumTemplate getAlbumTemplates(string sessionID)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public int[] getImages(string sessionID, int albumID, bool heavy, string password, string sitePassword)
        {

            throw new Exception("The method or operation is not implemented.");
        }

        public SMImageURLs getImageURLs(string sessionID, int imageID, int templateID, string password, string sitePassword)
        {
            throw new Exception("The method or operation is not implemented.");
         }

        public SMImageInfo getImageInfo(string sessionID, int imageID, string password, string sitePassword)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public SMImageEXIF getImageEXIF(string sessionID, int imageID, string password, string sitePassword)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public bool changeImageSettings(string sessionID, int imageID, SMImageSettings settings)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public bool changeImagePosition(string sessionID, int imageID, int position)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public int uploadImage(string sessionID, int albumID, string fileName, string caption, string keywords, string latitude, string longitude, string altitude)
        {
            //HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            //request.Method = "PUT";
            //request.Headers.Add("X-Smug-SessionID", sessionID);
            //request.Headers.Add("X-Smug-Version", API_VERSION);
            //request.Headers.Add("X-Smug-ResponseType", "JSON");
            //request.Headers.Add("X-Smug-AlbumID", albumID.ToString());
            ////request.Headers.Add("X-Smug-FileName", fileName);
            //using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            //{
            //    StreamReader reader = new StreamReader(response.GetResponseStream());
            //}
            throw new Exception("The method or operation is not implemented.");
        }

        public int uploadImageFromURL(string sessionID, int albumID, string caption, string URL, string keywords, string latitude, string longitude, string altitude, int byteCount, string MD5Sum)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public string deleteImage(string sessionID, int imageID)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public SMImageStats getImageStats(string sessionID, int imageID, int month)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public SMCategory[] getCategories(string sessionID, string nickName, string sitePassword)
        {
            JSONCategories categories = new JSONCategories();

            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.categories.get");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "NickName", nickName);
            appendArgumentToUrl(url, "SitePassword", sitePassword);

            categories = executeWebMethod<JSONCategories>(url.ToString());
            return categories.Categories;
        }

        public int createCategory(string sessionID, string name)
        {
            JSONCategoryID categoryID = new JSONCategoryID();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.categories.create");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "Name", name);

            categoryID = executeWebMethod<JSONCategoryID>(url.ToString());
            return categoryID.Category.id;
        }

        public bool deleteCategory(string sessionID, int categoryID)
        {
            JSONCategoryDeleteResponse response = new JSONCategoryDeleteResponse();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.categories.delete");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "CategoryID", categoryID.ToString());

            response = executeWebMethod<JSONCategoryDeleteResponse>(url.ToString());
            return response.Category;
        }

        public bool renameCategory(string sessionID, int categoryID, string name)
        {
            JSONCategoryRenameResponse response = new JSONCategoryRenameResponse();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.categories.rename");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "CategoryID", categoryID.ToString());
            appendArgumentToUrl(url, "Name", name);

            response = executeWebMethod<JSONCategoryRenameResponse>(url.ToString());
            return response.Category;
        }

        public SMSubCategory[] getSubCategories(string sessionID, int categoryID, string nickName, string sitePassword)
        {
            JSONSubCategories subCategories = new JSONSubCategories();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.subcategories.get");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "CategoryID", categoryID.ToString());
            appendArgumentToUrl(url, "NickName", nickName);
            appendArgumentToUrl(url, "SitePassword", sitePassword);

            subCategories = executeWebMethod<JSONSubCategories>(url.ToString());
            return subCategories.SubCategories;
        }

        public SMSubCategories[] getAllSubCategories(string sessionID, string nickName, string sitePassword)
        {
            JSONAllSubCategories subCategories = new JSONAllSubCategories();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.subcategories.getAll");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "NickName", nickName);
            appendArgumentToUrl(url, "SitePassword", sitePassword);

            subCategories = executeWebMethod<JSONAllSubCategories>(url.ToString());
            return subCategories.SubCategories;
        }

        public int createSubCategory(string sessionID, string name, int categoryID)
        {
            JSONSubCategoryID subCategoryID = new JSONSubCategoryID();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.subcategories.create");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "Name", name);
            appendArgumentToUrl(url, "CategoryID", categoryID.ToString());

            subCategoryID = executeWebMethod<JSONSubCategoryID>(url.ToString());
            return subCategoryID.SubCategory.id;
        }

        public bool deleteSubCategory(string sessionID, int subCategoryID)
        {
            JSONSubCategoryDeleteResponse response = new JSONSubCategoryDeleteResponse();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.subcategories.delete");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "SubCategoryID", subCategoryID.ToString());

            response = executeWebMethod<JSONSubCategoryDeleteResponse>(url.ToString());
            return response.SubCategory;
        }

        public bool renameSubCategory(string sessionID, int subCategoryID, string name)
        {
            JSONSubCategoryRenameResponse response = new JSONSubCategoryRenameResponse();
            StringBuilder url = new StringBuilder(API_URL + "?method=smugmug.subcategories.rename");
            appendArgumentToUrl(url, "SessionID", sessionID);
            appendArgumentToUrl(url, "SubCategoryID", subCategoryID.ToString());
            appendArgumentToUrl(url, "Name", name);

            response = executeWebMethod<JSONSubCategoryRenameResponse>(url.ToString());
            return response.SubCategory;
        }
        #endregion

        private static void appendArgumentToUrl(StringBuilder url, string argumentName, string argumentValue)
        {
            if (argumentValue != null)
                url.Append("&" + argumentName + "=" + HttpUtility.UrlEncode(argumentValue));
        }

        private static void appendArgumentToUrl(StringBuilder url, string argumentName, int? argumentValue)
        {
            if (argumentValue.HasValue)
                appendArgumentToUrl(url, argumentName, argumentValue.ToString());
        }

        private static void appendArgumentToUrl(StringBuilder url, string argumentName, bool? argumentValue)
        {
            if (argumentValue.HasValue)
                appendArgumentToUrl(url, argumentName, argumentValue.ToString());
        }

        private static void executeWebMethod(string url)
        {
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                StreamReader reader = new StreamReader(response.GetResponseStream());
                string respString = reader.ReadToEnd();
            }
        }

        private static T executeWebMethod<T>(string url)
        {
            T returnType = default(T);

            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                StreamReader reader = new StreamReader(response.GetResponseStream());
                string respString = reader.ReadToEnd();
                returnType = JavaScriptConvert.DeserializeObject<T>(respString);
            }
            return returnType;
        }

        #region JSON Response Structs

        private class JSONResponse
        {
            public string stat;
            public string method;
            public int code;
            public string message;
        }

        private class JSONResult : JSONResponse
        {
            public string result;
        }

        private class JSONLoginInfo : JSONResponse
        {
            public SMLoginInfo Login;
        }

        private class JSONSessionInfo : JSONResponse
        {
            public SMSessionInfo SessionID;
        }

        private class JSONUserTreeCategories : JSONResponse
        {
            public SMUserTreeCategory[] Categories;
        }

        private class JSONUserTransferStats : JSONResponse
        {
            public SMAlbumStats[] Albums;
        }

        private class JSONAlbums : JSONResponse
        {
            public SMAlbum[] Albums;
        }

        private class JSONAlbumInfo : JSONResponse
        {
            public SMAlbumInfo Album;
        }

        private class JSONAlbumID : JSONResponse
        {
            public SmugMugID Album;
        }

        private class JSONAlbumStats : JSONResponse
        {
            public SMAlbumStats Album;
        }

        private class JSONAlbumReSortResponse : JSONResponse
        {
            public bool Album;
        }

        private class JSONAlbumDeleteResponse : JSONResponse
        {
            public bool Album;
        }

        private class JSONCategories : JSONResponse
        {
            public SMCategory[] Categories;
        }

        private class JSONCategoryID : JSONResponse
        {
            public SmugMugID Category;
        }

        private class JSONCategoryDeleteResponse : JSONResponse
        {
            public bool Category;
        }

        private class JSONCategoryRenameResponse : JSONResponse
        {
            public bool Category;
        }

        private class JSONSubCategories : JSONResponse
        {
            public SMSubCategory[] SubCategories;
        }

        private class JSONAllSubCategories : JSONResponse
        {
            public SMSubCategories[] SubCategories;
        }

        private class JSONSubCategoryID : JSONResponse
        {
            public SmugMugID SubCategory;
        }

        private class JSONSubCategoryDeleteResponse : JSONResponse
        {
            public bool SubCategory;
        }

        private class JSONSubCategoryRenameResponse : JSONResponse
        {
            public bool SubCategory;
        }

        #endregion

    }
}
